#include "DetectorRegiones.h"
#include "Hough.h"
#include "preprocesamiento.h"
#include "corners.h"
#include "utils.h"
#include "OCR.h"
#include <iostream>
#include <fstream>

#define APROXIMATED_MIN_PLATE_PERIMETER 250

DetectorRegiones::DetectorRegiones(void)
{
	cornersGlobal = new vector<vector<Point2f> >();
}


DetectorRegiones::~DetectorRegiones(void)
{
	delete cornersGlobal;
} 



void DetectorRegiones::hallarContornosPosibles(Mat src,vector<vector<Point> > &contornosFiltrados,int perimetro_maximo_aproximado){

	/***********************************   CONTOUR   ************************************************************/
	vector<Vec4i> hierarchy;
	vector<vector<Point> > contours;
	int t = (double)getTickCount();
	/// Find contours
	findContours( src, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE, Point(0, 0) );
	/// Draw contours

	for (size_t c =  0; c < contours.size();c++)
	{
		t = (double) getTickCount();
		vector<Point> contorno = contours[c];
		if (contorno.size() < 4) continue;
		int perimetro = (int)arcLength(contorno,true);
		Rect contornoBoundingRect = boundingRect(contorno);
		//if (perimetro < APROXIMATED_MIN_PLATE_PERIMETER) continue;

		perimetro = contornoBoundingRect.width*2 + contornoBoundingRect.height*2;

		if (contornoBoundingRect.height > src.rows/3) continue;
		if (contornoBoundingRect.height > contornoBoundingRect.width) continue;
		if (perimetro < APROXIMATED_MIN_PLATE_PERIMETER) continue;
		if (perimetro > perimetro_maximo_aproximado) continue;
		contornosFiltrados.push_back(contorno);
	}

	/***********************************   CONTOUR   ************************************************************/
}

void DetectorRegiones::filtrarVentanas(vector<vector<Point2f> > cuadrilateros,vector<vector<Point2f> > &cuadrilaterosFiltrados,int perimetro_maximo_aproximado){
	int distanciaMinimaLinea = 40;
	for(size_t i=0;i<cuadrilateros.size();i++){ 
		double maxCosine = 0;
		vector<Point2f> poligono = cuadrilateros[i];
		int perimetroVentana = (int)arcLength(poligono,true);
		if (perimetroVentana > perimetro_maximo_aproximado) continue;
		for( int j = 2; j < 5; j++ )
		{
			// find the maximum cosine of the angle between joint edges
			double cosine = fabs(angleBetween(poligono[j%4], poligono[j-2], poligono[j-1]));
			maxCosine = MAX(maxCosine, cosine);
		}
		// (all angles are ~90 degree) 
		// Error de 10 grados sexagesimales aprox
		double ancho = distanceBetweenF(poligono[0],poligono[1]);
		double alto = distanceBetweenF(poligono[0],poligono[3]);
		if( maxCosine < 0.17364 && esParalelogramo(poligono,12) && cumpleRatio(ancho,alto,2,0.3,0.6))
			cuadrilaterosFiltrados.push_back(cuadrilateros[i]);
	}  
}
bool DetectorRegiones::hallarCuadrilateros(Mat src,Hough houghModule,vector<Point> contorno,vector<vector<Point2f> > &corners, vector<vector<vector<Point> > > &lineasxcontorno){
	int t = (double)getTickCount();
	houghModule.Transform(contorno,src.cols,src.rows);
	vector<Vec4i> lines = houghModule.GetLines(deltaThresholdHough);
	if (lines.size() < 4) {
		agregarLineas(lineasxcontorno,lines);
		return false;
	}

	vector<Vec4i> lines2; //Lineas pero eliminando los paralelos
	//pone las lineas paralelas en la lista negra para no procesarlas de nuevo
	eliminarParalelas(lines,lines2);
	agregarLineas(lineasxcontorno,lines);
	if (lines2.size() < 4) 
		return false;
	hallarPoligonosGrafo(src,lines2,corners);
	//Para transformacion de persepctiva ordena los puntos del cuadrilatero en sentido horario desde top left hasta botom left
	for(size_t i=0;i<corners.size();i++){  
		Point2f center(0,0);  
		vector<Point2f> esquinas=corners[i];
		if(esquinas.size()<4)continue;  
		int ejeX[4] = {(int)esquinas[0].x,(int)esquinas[1].x,(int)esquinas[2].x,(int)esquinas[3].x};
		int ejeY[4] = {(int)esquinas[0].y,(int)esquinas[1].y,(int)esquinas[2].y,(int)esquinas[3].y};
		bool lineaX = ejeX[0] == ejeX[1] && ejeX[0] == ejeX[2] && ejeX[0] == ejeX[3];
		bool lineaY = ejeY[0] == ejeY[1] && ejeY[0] == ejeY[2] && ejeY[0] == ejeY[3];
		if (lineaX || lineaY) continue;
		for(size_t j=0;j<corners[i].size();j++){  
			center += corners[i][j];  
		}  
		center *= (1. / corners[i].size());  
		sortCorners(corners[i], center);  
	}  
	return true;
}

void DetectorRegiones::hallarImagenesContornos(vector<Mat> &contornosImagen){
	int perimetro_maximo_aproximado = (processedImage.size().height + processedImage.size().width);
	Mat gray = processedImage.clone();
	vector<vector<Point> > contornosFiltrados;
	hallarContornosPosibles(gray,contornosFiltrados,perimetro_maximo_aproximado);
	Hough houghModule;
	vector<vector<vector<Point> > > lineasxcontorno;
	for (size_t c =  0; c < contornosFiltrados.size();c++)
	{
		vector<Point> contorno = contornosFiltrados[c];
		vector<vector<Point2f> > corners;  
		if (!hallarCuadrilateros(gray,houghModule,contorno,corners,lineasxcontorno)) {
			houghModule.liberar();
			continue;
		}
		houghModule.liberar();
		filtrarVentanas(corners,(*cornersGlobal),perimetro_maximo_aproximado);
	}
	for (int i=0; i < contornosFiltrados.size(); i++){
		Mat imgContorno = Mat::zeros(gray.size(),CV_8UC3);
		drawContours(imgContorno,contornosFiltrados,i,Scalar(255,255,255),2);
		polylines(imgContorno,lineasxcontorno[i],false,Scalar(0,0,255));
		contornosImagen.push_back(imgContorno);
	}
}
void DetectorRegiones::hallarImagenesCorners(vector<Mat> &cornersImagen){
	Mat gray = processedImage.clone();
	for (size_t i =  0; i < cornersGlobal->size();i++)
	{
		vector<Point2f> cuadrilatero = cornersGlobal->at(i);
		vector<Point> cuadrilateroEntero;
		convertirPoint2fToPoint(cuadrilatero,cuadrilateroEntero);
		vector<vector<Point> > aux;
		aux.push_back(cuadrilateroEntero);
		Mat mascara = Mat::zeros(input_image_color.size(),CV_8UC1 );
		fillPoly(mascara,aux,Scalar(255));
		Mat imagenResultante = processedImage.clone();
		cvtColor(imagenResultante,imagenResultante,CV_GRAY2BGR);
		copyTo(imagenResultante,input_image_color,mascara);
		//input_image_color.copyTo(imagenResultante,mascara);
		cornersImagen.push_back(imagenResultante);
	}
}

Mat DetectorRegiones::run(bool esVideo){
	//resultadosExp.open ("resultExp.txt");
	//resultadosExp <<"Archivo: "<<  this->nombreArchivo << endl << endl;
	vector<Vec4i> lines;
	vector<vector<Point> > contornosFiltrados;
	bool imagenNoVideo = !esVideo;
	cornersGlobal->erase(cornersGlobal->begin(), cornersGlobal->end());
	int min_thresholdHough = 0;
	int perimetro_maximo_aproximado = (processedImage.size().height + processedImage.size().width);
	//imshow("Canny", gray);
	Mat gray = processedImage.clone();
	Mat blackAux;

	hallarContornosPosibles(gray,contornosFiltrados,perimetro_maximo_aproximado);

	/***********************************   HOUGH SOBRE CADA CONTOUR   ************************************************************/

	Hough houghModule;
	vector<vector<vector<Point> > > lineasxcontorno;
	for (size_t c =  0; c < contornosFiltrados.size();c++)
	{
		vector<Point> contorno = contornosFiltrados[c];
		vector<vector<Point2f> > corners;  
		if (!hallarCuadrilateros(gray,houghModule,contorno,corners,lineasxcontorno)) {
			houghModule.liberar();
			continue;
		}
		houghModule.liberar();
		/***********************************   Filtro de ventanas   ************************************************************/
		filtrarVentanas(corners,(*cornersGlobal),perimetro_maximo_aproximado);
		/***********************************   Filtro de ventanas   ************************************************************/
	}
	Mat color = input_image_color.clone();
	for (size_t i = 0; i < (*cornersGlobal).size(); i++)
	{
		for (size_t j = 0; j < (*cornersGlobal)[i].size()-1; j++){
			line( color, (*cornersGlobal)[i][j],(*cornersGlobal)[i][j+1], Scalar(255,0,0), 4, 8);
			circle(color,(*cornersGlobal)[i][j],8,Scalar(255,255,255),3,8);
		}
		line( color, (*cornersGlobal)[i][3],(*cornersGlobal)[i][0], Scalar(255,0,0), 4, 8);
		circle(color,(*cornersGlobal)[i][3],8,Scalar(255,255,255),3,8);
	}
	/***********************************   HOUGH SOBRE CADA CONTOUR   ************************************************************/

	/*
	if (imagenNoVideo){
	namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
	imshow("Deteccion",color);
	//imwrite("intermediateSteps/contours_" + nombreArchivo,drawing);
	//imwrite("intermediateSteps/hough_"+ nombreArchivo,result);
	imwrite("intermediateSteps/canny_"+ nombreArchivo,gray);
	imwrite("intermediateSteps/detectedWindows_"+ nombreArchivo,color);
	}*/
	detectado = (cornersGlobal->size() > 0);
	resultadosExp.close();
	return color;
}
Mat DetectorRegiones::dibujarCorners(){
	for (size_t i = 0; i < (*cornersGlobal).size(); i++)
	{
		for (size_t j = 0; j < (*cornersGlobal)[i].size()-1; j++){
			line( input_image_color, (*cornersGlobal)[i][j],(*cornersGlobal)[i][j+1], Scalar(255,0,0), 4, 8);
			circle(input_image_color,(*cornersGlobal)[i][j],8,Scalar(255,255,255),3,8);
		}
		line( input_image_color, (*cornersGlobal)[i][3],(*cornersGlobal)[i][0], Scalar(255,0,0), 4, 8);
		circle(input_image_color,(*cornersGlobal)[i][3],8,Scalar(255,255,255),3,8);
	}
	return input_image_color;
}
